﻿using Irony.Parsing;
using NMF.Transformations;
using NMF.Transformations.Simples;
using NMF.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NMF.Languages.AnyText.ClassConstruction
{
    public class ClassBuilder : Transformation
    {

        public void AugmentGrammar(AnyTextGrammar grammar, TypeSystem typeSystem)
        {
            TransformationEngine.Process<AnyTextGrammar, TypeSystem>(grammar, typeSystem, this);
        }

        protected override IEnumerable<GeneralTransformationRule> CreateAllTransformationRules()
        {
            yield return new ClassAugmentTransformationRule();
            yield return new NonTerminalInitializationRule();
            yield return new BnfTermListRule();
            yield return new TypedNonterminalInitializationRule();
            yield return new DataTypeTerminalRule();
            yield return new IdentifierRule();
            yield return new IdentifierReferenceRule();
            yield return new RuleInitializationRule();
        }

        class ClassAugmentTransformationRule : SimpleVoidTransformationRule<AnyTextGrammar, TypeSystem>
        {
            public override void Init(AnyTextGrammar grammar, TypeSystem typeSystem, ITransformationContext context)
            {
                context.Bag.Grammar = grammar;
                context.Bag.TypeSystem = typeSystem;
            }

            public override void RegisterRequirements()
            {
                Call(Rule<RuleInitializationRule>(),
                    (g,ts) => g.Root as RuleNonTerminal, (g,ts) => new ClassBuilderContext());
            }
        }

        class NonTerminalInitializationRule : SimpleTransformationRule<NonTerminal, ClassBuilderContext, ClassBuilderContext>
        {
            public override ClassBuilderContext CreateOutput(NonTerminal input, ClassBuilderContext inContext, ITransformationContext context)
            {
                return inContext;
            }

            public override void RegisterRequirements()
            {
                CallManyOutputSensitive(Rule<BnfTermListRule>(), GetChildTerms);
            }

            private IEnumerable<Tuple<BnfTermList, ClassBuilderContext>> GetChildTerms(NonTerminal input, ClassBuilderContext inContext, ClassBuilderContext outContext)
            {
                if (input.Rule != null)
                {
                    foreach (var item in input.Rule.Data)
                    {
                        yield return new Tuple<BnfTermList, ClassBuilderContext>(item, outContext.TakeBnfList(item));
                    }
                }
            }
        }

        class BnfTermListRule : SimpleTransformationRule<BnfTermList, ClassBuilderContext, ClassBuilderContext>
        {
            public override ClassBuilderContext CreateOutput(BnfTermList input, ClassBuilderContext inContext, ITransformationContext context)
            {
                return inContext;
            }

            public override void RegisterRequirements()
            {
                RequireMany(Rule<NonTerminalInitializationRule>(),
                    CreateChildContexts);
            }

            private IEnumerable<Tuple<NonTerminal, ClassBuilderContext>> CreateChildContexts(BnfTermList termList, ClassBuilderContext context)
            {
                return termList.AllOfType<NonTerminal>().PairWithConstant(context);
            }
        }

        class TypedNonterminalInitializationRule : AbstractTransformationRule<TypedNonTerminal, ClassBuilderContext, ClassBuilderContext>
        {
            public override void Transform(TypedNonTerminal input, ClassBuilderContext inContext, ClassBuilderContext outContext, ITransformationContext context)
            {
                if (input.Property != null)
                {
                    var prop = input.Property;
                    if (prop.Property == null)
                    {
                        prop.Property = inContext.CurrentClass.GetProperty(prop.Name);
                        if (prop.IsCollection)
                        {
                            prop.Property.IsCollectionProperty = true;
                            prop.Property.CollectionType = input.Type;
                        }
                        else
                        {
                            prop.Property.Type = input.Type;
                        }
                    }
                    else
                    {
                        //prop.Property.SetIsValidFor(outContext.CurrentClass);
                    }
                }
            }

            public override void RegisterRequirements()
            {
                MarkInstantiatingFor(Rule<NonTerminalInitializationRule>());
            }
        }

        class DataTypeTerminalRule : SimpleTransformationRule<DataTypeTerminal, ClassBuilderContext, ClassBuilderContext>
        {
            public override ClassBuilderContext CreateOutput(DataTypeTerminal input, ClassBuilderContext context, ITransformationContext context2)
            {
                return context;
            }

            public override void RegisterRequirements()
            {
                MarkInstantiatingFor(Rule<TypedNonterminalInitializationRule>());
            }
        }

        class IdentifierRule : SimpleTransformationRule<Identifier, ClassBuilderContext, ClassBuilderContext>
        {
            public override ClassBuilderContext CreateOutput(Identifier input, ClassBuilderContext context, ITransformationContext context2)
            {
                input.AddPossibleIdentifiedClass(context.CurrentClass);
                return context;
            }

            public override void RegisterRequirements()
            {
                MarkInstantiatingFor(Rule<DataTypeTerminalRule>());
            }
        }

        class IdentifierReferenceRule : SimpleTransformationRule<IdentifierReference, ClassBuilderContext, ClassBuilderContext>
        {
            public override ClassBuilderContext CreateOutput(IdentifierReference input, ClassBuilderContext context, ITransformationContext context2)
            {
                return context.SwitchContextClass(input.Identifier.IdentifiedClass);
            }

            public override void RegisterRequirements()
            {
                MarkInstantiatingFor(Rule<TypedNonterminalInitializationRule>());

                OutputDelayLevel = 1;
            }
        }

        class RuleInitializationRule : SimpleTransformationRule<RuleNonTerminal, ClassBuilderContext, ClassBuilderContext>
        {
            public override ClassBuilderContext CreateOutput(RuleNonTerminal input, ClassBuilderContext inContext, ITransformationContext context)
            {
                if (input.IsInheritance || input.IsProperty || inContext.CurrentClass == null)
                {
                    var typeSystem = context.Bag.TypeSystem as TypeSystem;
                    var c = typeSystem.GetClass(input.Name);
                    input.SetType(c);
                    return inContext.SwitchContextClass(c);
                }
                else
                {
                    return inContext;
                }
            }

            public override void Transform(RuleNonTerminal input, ClassBuilderContext inContext, ClassBuilderContext outContext, ITransformationContext context)
            {
                if (inContext != outContext)
                {
                    if (inContext.CurrentClass != null)
                    {
                        //outContext.CurrentClass.??(inContext.CurrentClass);
                    }
                    input.SetType(outContext.CurrentClass);
                }
            }

            public override void RegisterRequirements()
            {
                MarkInstantiatingFor(Rule<TypedNonterminalInitializationRule>());
            }
        }

    }
}
